package com.audaque.springboot.foshanupload.schedule.abstra;

import com.audaque.springboot.foshanupload.schedule.container.DynamicScheduleContainer;
import com.audaque.springboot.foshanupload.schedule.iface.DynamicScheduleIface;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

@Slf4j
public abstract class AbstractDynamicSchedule<T extends DynamicScheduleIface> implements SchedulingConfigurer {

    private DynamicScheduleContainer<T> dynamicScheduleContainer;
    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    private final String CLASS_NAME = getClass().getSimpleName();

    /**
     * 获取用于执行任务的线程池
     */

    protected ExecutorService getWorkerExecutor() {
        return scheduledExecutorService;
    }

    /**
     * 获取所有的任务信息
     */
    protected abstract List<T> listTaskInfo();


    /**
     * 做具体的任务逻辑
     */
    protected abstract void doProcess(T taskInfo);


    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        dynamicScheduleContainer = new DynamicScheduleContainer<>(taskRegistrar);
        // 每隔 100ms 调度一次，用于读取所有任务
        taskRegistrar.addFixedDelayTask(this::scheduleTask, 1000);
    }

    /**
     * 调度任务，加载所有任务并注册
     */
    private void scheduleTask() {
        List<T> ts = listTaskInfo();
        for (T t : ts) {
            dynamicScheduleContainer.checkTask(t, new TriggerTask(() ->
                    this.execute(t), triggerContext -> new CronTrigger(t.getCron()).nextExecutionTime(triggerContext)
            ));
        }
    }

    private void execute(final T taskInfo) {
        final long taskId = taskInfo.getId();

        try {
            Semaphore semaphore = dynamicScheduleContainer.getSemaphore(taskId);
            if (Objects.isNull(semaphore)) {
                log.error("{} semaphore is null, taskId: {}", CLASS_NAME, taskId);
                return;
            }
            if (semaphore.tryAcquire(3, TimeUnit.SECONDS)) {
                try {
                    getWorkerExecutor().execute(() -> doProcess(taskInfo));
                } finally {
                    semaphore.release();
                }
            } else {
                log.warn("{} too many executor, taskId: {}", CLASS_NAME, taskId);
            }
        } catch (InterruptedException e) {
            log.warn("{} interruptedException error, taskId: {}", CLASS_NAME, taskId);
        } catch (Exception e) {
            log.error("{} execute error, taskId: {}", CLASS_NAME, taskId, e);
        }
    }
}
